#include <cppunit/TestAssert.h>
#include <cppunit/TestFixture.h>
#include <cppunit/TestResult.h>
#include <cppunit/TestSuite.h>
#include <cppunit/TestCaller.h>

#include <cppunit/extensions/AutoRegisterSuite.h>
#include <cppunit/extensions/HelperMacros.h>

#include <terms/util/to_string.hpp>
#include <terms/dl/dlterms.hpp>

class DLDataSomeRestrictionTest
	: public CppUnit::TestFixture
{
private:
	terms::dl::DLTermFactory<> *factory;

public:
	void setUp()
	{
		factory = new terms::dl::DLTermFactory<>();
	}

public:
	void tearDown()
	{
		delete factory;
	}

public:
	void testAllocate()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLLiteral<> *a = factory->create<DLLiteral<>>("a");
		const DLDataSomeRestriction<> *all = factory->create<DLDataSomeRestriction<>>("r", a);
		CPPUNIT_ASSERT(all != nullptr);
	}

	void testSingleton()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLLiteral<> *a = factory->create<DLLiteral<>>("a");
		const DLDataSomeRestriction<> *all1 = factory->create<DLDataSomeRestriction<>>("r", a);
		const DLDataSomeRestriction<> *all2 = factory->create<DLDataSomeRestriction<>>("r", a);
		CPPUNIT_ASSERT(all1 == all2);
		CPPUNIT_ASSERT(*all1 == *all2);
	}

	void testTermHierarchy()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLLiteral<> *a = factory->create<DLLiteral<>>("a");
		const DLDataSomeRestriction<> *all = factory->create<DLDataSomeRestriction<>>("r", a);
		CPPUNIT_ASSERT(dynamic_cast<const DLClassExpression<> *>(all) != nullptr);
		CPPUNIT_ASSERT(dynamic_cast<const DLSomeRestriction<DLDataRange<>> *>(all) != nullptr);
		CPPUNIT_ASSERT(dynamic_cast<const DLTerm<> *>(all) != nullptr);
		CPPUNIT_ASSERT(dynamic_cast<const Term *>(all) != nullptr);
	}

	void testOrder()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLLiteral<> *a = factory->create<DLLiteral<>>("a");
		const DLLiteral<> *b = factory->create<DLLiteral<>>("b");
		const DLDataSomeRestriction<> *allra = factory->create<DLDataSomeRestriction<>>("r", a);
		const DLDataSomeRestriction<> *allrb = factory->create<DLDataSomeRestriction<>>("r", b);
		const DLDataSomeRestriction<> *allsa = factory->create<DLDataSomeRestriction<>>("s", a);
		const DLDataSomeRestriction<> *allsb = factory->create<DLDataSomeRestriction<>>("s", b);

		CPPUNIT_ASSERT(*allra < *allrb);
		CPPUNIT_ASSERT(!(*allrb < *allra));

		CPPUNIT_ASSERT(*allra < *allsa);
		CPPUNIT_ASSERT(!(*allsa < *allra));

		CPPUNIT_ASSERT(*allra < *allsb);
		CPPUNIT_ASSERT(!(*allsa < *allrb));

		CPPUNIT_ASSERT(*allrb < *allsa);
		CPPUNIT_ASSERT(!(*allsa < *allrb));

		CPPUNIT_ASSERT(*allrb < *allsb);
		CPPUNIT_ASSERT(!(*allsb < *allrb));

		CPPUNIT_ASSERT(*allsa < *allsb);
		CPPUNIT_ASSERT(!(*allsb < *allsa));
	}

	void testEquality()
	{
		using namespace terms;
		using namespace terms::dl;

		const DLLiteral<> *a = factory->create<DLLiteral<>>("a");
		const DLLiteral<> *b = factory->create<DLLiteral<>>("b");

		const DLDataSomeRestriction<> *allra = factory->create<DLDataSomeRestriction<>>("r", a);
		const DLDataSomeRestriction<> *allra2 = factory->create<DLDataSomeRestriction<>>("r", a);
		const DLDataSomeRestriction<> *allrb = factory->create<DLDataSomeRestriction<>>("r", b);
		const DLDataSomeRestriction<> *allsa = factory->create<DLDataSomeRestriction<>>("s", a);
		const DLDataSomeRestriction<> *allsb = factory->create<DLDataSomeRestriction<>>("s", b);

		CPPUNIT_ASSERT(allra == allra2);
		CPPUNIT_ASSERT(allra2 == allra);
		CPPUNIT_ASSERT(*allra == *allra2);
		CPPUNIT_ASSERT(*allra2 == *allra);

		CPPUNIT_ASSERT(!(*allra != *allra2));
		CPPUNIT_ASSERT(!(*allra2 != *allra));

		CPPUNIT_ASSERT(allra != allrb);
		CPPUNIT_ASSERT(allrb != allra);
		CPPUNIT_ASSERT(*allra != *allrb);
		CPPUNIT_ASSERT(*allrb != *allra);

		CPPUNIT_ASSERT(!(*allra == *allrb));
		CPPUNIT_ASSERT(!(*allrb == *allra));

		CPPUNIT_ASSERT(allra != allsa);
		CPPUNIT_ASSERT(allsa != allra);
		CPPUNIT_ASSERT(*allra != *allsa);
		CPPUNIT_ASSERT(*allsa != *allra);

		CPPUNIT_ASSERT(!(*allra == *allsa));
		CPPUNIT_ASSERT(!(*allsa == *allra));

		CPPUNIT_ASSERT(allra != allsb);
		CPPUNIT_ASSERT(allsb != allra);
		CPPUNIT_ASSERT(*allra != *allsb);
		CPPUNIT_ASSERT(*allsb != *allra);

		CPPUNIT_ASSERT(!(*allra == *allsb));
		CPPUNIT_ASSERT(!(*allsb == *allra));
	}


	static CppUnit::Test *
	suite()
	{
		std::unique_ptr<CppUnit::TestSuite> suite_ptr(new CppUnit::TestSuite("DLDataSomeRestrictionTest"));

		suite_ptr->addTest(new CppUnit::TestCaller<DLDataSomeRestrictionTest>("testAllocate", &DLDataSomeRestrictionTest::testAllocate));
		suite_ptr->addTest(new CppUnit::TestCaller<DLDataSomeRestrictionTest>("testSingleton", &DLDataSomeRestrictionTest::testSingleton));
		suite_ptr->addTest(new CppUnit::TestCaller<DLDataSomeRestrictionTest>("testOrder", &DLDataSomeRestrictionTest::testOrder));
		suite_ptr->addTest(new CppUnit::TestCaller<DLDataSomeRestrictionTest>("testEquality", &DLDataSomeRestrictionTest::testEquality));
		suite_ptr->addTest(new CppUnit::TestCaller<DLDataSomeRestrictionTest>("testTermHierarchy", &DLDataSomeRestrictionTest::testTermHierarchy));

		return suite_ptr.release();
	}
};

CPPUNIT_TEST_SUITE_REGISTRATION(DLDataSomeRestrictionTest);

#include "test_main.cc"
